package influx

import (
	"fmt"
	client "github.com/influxdata/influxdb-client-go/v2"
	"github.com/influxdata/influxdb-client-go/v2/api/write"
	"github.com/ochinchina/supervisord/config"
	"github.com/shirou/gopsutil/disk"
	"github.com/shirou/gopsutil/load"
	"github.com/shirou/gopsutil/mem"
	"github.com/shirou/gopsutil/process"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/cast"
	"net"
	"os"
	"strconv"
	"time"
)

var serverUrl = ""
var authToken = ""
var org = ""
var bucket = ""
var enableInflux = true

func InitInflux(c *config.Config) {
	serverUrl = c.ParseConfigStringExpression(c.Config.InfluxConfig.ServerURL)
	authToken = c.ParseConfigStringExpression(c.Config.InfluxConfig.AuthToken)
	org = c.ParseConfigStringExpression(c.Config.InfluxConfig.Org)
	bucket = c.ParseConfigStringExpression(c.Config.InfluxConfig.Bucket)

	if serverUrl == "" {
		enableInflux = false
		log.Infof("influx config properties not set")
	} else {
		log.Infof("Init influx config properties success")
	}
	log.Debugf("serverurl: %s, authToken:%s, org:%s, bucket:%s, enableInflux:%s", serverUrl, authToken, org, bucket, enableInflux)
}

func WritesPoints(pt *write.Point) {
	if !enableInflux {
		return
	}

	cli := connInflux()
	writeAPI := cli.WriteAPI(org, bucket)
	writeAPI.WritePoint(pt)
	writeAPI.Flush()
	cli.Close()
}

func WritesMemPoints(data *mem.VirtualMemoryStat) {
	if !enableInflux {
		return
	}

	tags := getTags("mem", "mem0")
	fields := map[string]interface{}{
		"total":       int64(data.Total), //因为不支持uint64 强转一下
		"available":   int64(data.Available),
		"used":        int64(data.Used),
		"usedPercent": data.UsedPercent,
		"buffers":     int64(data.Buffers),
		"cached":      int64(data.Cached),
	}

	pt := client.NewPoint("memInfo", tags, fields, time.Now())
	WritesPoints(pt)
	log.Infof("insert mem info to influxdb success")
}

func WritesDiskPoints(par disk.PartitionStat, data *disk.UsageStat) {
	if !enableInflux {
		return
	}

	tags := getTags("path", par.Mountpoint)
	fields := map[string]interface{}{
		"total":             int64(data.Total),
		"free":              int64(data.Free),
		"used":              int64(data.Used),
		"usedPercent":       data.UsedPercent,
		"inodesTotal":       int64(data.InodesTotal),
		"inodesUsed":        int64(data.InodesUsed),
		"inodesFree":        int64(data.InodesFree),
		"inodesUsedPercent": data.InodesUsedPercent,
	}
	pt := client.NewPoint("diskInfo", tags, fields, time.Now())
	WritesPoints(pt)
	log.Println("insert disk info success")
}

func WritesCpuPoints(percent []float64) {
	if !enableInflux {
		return
	}

	tags := getTags("cpu", "cpu0")
	fields := map[string]interface{}{
		"cpu_per": percent[0],
	}
	pt := client.NewPoint("cpuInfo", tags, fields, time.Now())
	WritesPoints(pt)
	log.Println("insert cpu info to influxdb success")
}

func WritesLoadPoints(avgStat *load.AvgStat) {
	if !enableInflux {
		return
	}

	tags := getTags("load", "load")
	fields := map[string]interface{}{
		"Load1":  avgStat.Load1,
		"Load5":  avgStat.Load5,
		"Load15": avgStat.Load15,
	}
	pt := client.NewPoint("loadInfo", tags, fields, time.Now())
	WritesPoints(pt)
	log.Println("insert load info to influxdb success")
}

func WriteProcessPoint(name string, cpu float64, mem float32, proc *process.Process) {
	if !enableInflux {
		return
	}

	tags := getTags("process", name)
	tags["pid"] = fmt.Sprint(proc.Pid)
	iostat, err := proc.IOCounters()
	fields := map[string]interface{}{
		"cpu":    float64Rand(cpu, 4),
		"memory": float64Rand(float64(mem), 4),
	}
	if err == nil {
		fields["readCount"] = int64(iostat.ReadCount)
		fields["writeCount"] = int64(iostat.WriteCount)
		fields["readBytes"] = int64(iostat.ReadBytes)
		fields["writeBytes"] = int64(iostat.WriteBytes)
	}
	pt := client.NewPoint("processInfo", tags, fields, time.Now())
	WritesPoints(pt)
	log.Println("insert process info to influxdb success")
}

func connInflux() client.Client {
	cli := client.NewClientWithOptions(serverUrl, authToken,
		client.DefaultOptions().SetBatchSize(20))
	return cli
}

func getTags(tagKey string, tagVal string) map[string]string {
	tags := map[string]string{tagKey: tagVal}
	tags["ip"] = getIPs()[0]
	tags["host"] = getHost()
	return tags
}

func getIPs() (ips []string) {
	interfaceAddr, err := net.InterfaceAddrs()
	if err != nil {
		log.Errorf("fail to get net interface addrs: %v", err)
		return ips
	}

	for _, address := range interfaceAddr {
		ipNet, isValidIpNet := address.(*net.IPNet)
		if isValidIpNet && !ipNet.IP.IsLoopback() {
			if ipNet.IP.To4() != nil {
				ips = append(ips, ipNet.IP.String())
			}
		}
	}

	return ips
}

func getHost() (host string) {
	hostname, err := os.Hostname()
	if err != nil {
		log.Errorf("fail to get host: %v", err)
		return host
	}
	return hostname
}

func float64Rand(v float64, dig int) float64 {
	cDig := strconv.Itoa(dig)
	val := fmt.Sprintf("%0."+cDig+"f", v)
	return cast.ToFloat64(val)
}
